package com.imooc.follow.service;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import javax.annotation.Resource;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import com.imooc.commons.constant.ApiConstant;
import com.imooc.commons.constant.RedisKeyConstant;
import com.imooc.commons.exception.ParameterException;
import com.imooc.commons.model.domain.ResultInfo;
import com.imooc.commons.model.pojo.Follow;
import com.imooc.commons.model.vo.ShortDinerInfo;
import com.imooc.commons.model.vo.SignInDinerInfo;
import com.imooc.commons.utils.AssertUtil;
import com.imooc.commons.utils.ResultInfoUtil;
import com.imooc.follow.mapper.FollowMapper;

import cn.hutool.core.util.StrUtil;


import static cn.hutool.core.bean.BeanUtil.fillBeanWithMap;

/**
 * \* @author: bear
 * \* Date: 2021-08-04-10:59
 * \* describe:
 */
@Service
public class FollowService {

	@Value("${service.name.ms-oauth-server}")
	private String oauthServerName;
	@Value("${service.name.ms-diners-server}")
	private String dinersServerName;
	@Value("${service.name.ms-feeds-server}")
	private String feedsServerName;

	@Resource
	private FollowMapper followMapper;
	@Resource
	private RestTemplate restTemplate;
	@Resource
	private RedisTemplate redisTemplate;

	/**
	 * 关注/取关
	 *
	 * @param followDinerId 关注的食客id
	 * @param isFollowed 是否关注 1=关注，0=取消
	 * @param accessToken 登录token
	 * @param path 访问地址
	 *
	 * @return
	 */
	@Transactional(rollbackFor = Exception.class)
	public ResultInfo follow(Integer followDinerId, int isFollowed, String accessToken, String path) {
		//是否选择了关注对象
		AssertUtil.isTrue(followDinerId == null || followDinerId < 1, "请选择要关注的人");
		//获取登录信息
		SignInDinerInfo dinerInfo = loadSignInDinerInfo(accessToken);
		//获取当前登录用户与需要关注用户的关注信息
		Follow follow = followMapper.selectFollow(dinerInfo.getId(), followDinerId);
		//如果没有关注信息，且要进行关注操作
		if (follow == null && isFollowed == 1) {
			//添加关注信息
			int count = followMapper.save(dinerInfo.getId(), followDinerId);
			//添加关注列表到redis中
			if (count == 1) {
				addToRedisSet(dinerInfo.getId(), followDinerId);
				sendSaveOrRemoveFeed(followDinerId,accessToken,1);
			}
			return ResultInfoUtil.build(ApiConstant.SUCCESS_CODE, "关注成功", path, "关注成功");
		}

		//isValid值为0，那么取关，如果isValid =1 那么就是关注中
		//如果有关注信息，且目前处于取关状态，且要进行关注操作，
		if (follow != null && follow.getIsValid() == 0 && isFollowed == 1) {
			//重新关注
			int count = followMapper.update(follow.getId(), isFollowed);
			//添加关注列表
			if (count == 1) {
				addToRedisSet(dinerInfo.getId(), followDinerId);
				sendSaveOrRemoveFeed(followDinerId,accessToken,1);
			}
			return ResultInfoUtil.build(ApiConstant.SUCCESS_CODE, "关注成功", path, "关注成功");
		}

		//如果有关注信息，且目前处于关注状态，且要进行取关操作
		if (follow != null && follow.getIsValid() == 1 && isFollowed == 0) {
			//取关
			int count = followMapper.update(follow.getId(), isFollowed);
			//移除redis关注列表
			removeFromRedisSet(dinerInfo.getId(), followDinerId);
			//移除feed
			sendSaveOrRemoveFeed(followDinerId,accessToken,0);
			return ResultInfoUtil.build(ApiConstant.SUCCESS_CODE, "成功取关", path, "成功取关");
		}

		return ResultInfoUtil.buildSuccess(path, "操作成功");
	}

	/**
	 * 移除 Redis 关注列表
	 *
	 * @param dinerId
	 * @param followDinerId
	 */
	private void removeFromRedisSet(Integer dinerId, Integer followDinerId) {
		redisTemplate.opsForSet().remove(RedisKeyConstant.following.getKey() + dinerId, followDinerId);
		redisTemplate.opsForSet().remove(RedisKeyConstant.followers.getKey() + followDinerId, dinerId);
	}

	/**
	 * 添加关注列表到redis中
	 *
	 * @param dinerId
	 * @param followDinerId
	 */
	private void addToRedisSet(Integer dinerId, Integer followDinerId) {
		redisTemplate.opsForSet().add(RedisKeyConstant.following.getKey() + dinerId, followDinerId);
		redisTemplate.opsForSet().add(RedisKeyConstant.followers.getKey() + followDinerId, dinerId);
	}

	/**
	 * 获取用户登录信息
	 *
	 * @param accessToken
	 *
	 * @return
	 */
	private SignInDinerInfo loadSignInDinerInfo(String accessToken) {
		//是否有token
		AssertUtil.mustLogin(accessToken);
		String url = oauthServerName + "/user/me?access_token={accessToken}";
		ResultInfo resultInfo = restTemplate.getForObject(url, ResultInfo.class, accessToken);
		if (resultInfo.getCode() != ApiConstant.SUCCESS_CODE) {
			throw new ParameterException(resultInfo.getCode(), resultInfo.getMessage());
		}

		//这里的data是一个linkedHashmap
		SignInDinerInfo dinerInfo = fillBeanWithMap((LinkedHashMap)resultInfo.getData(), new SignInDinerInfo(), false);
		return dinerInfo;
	}

	public ResultInfo findCommonsFriends(Integer dinerId, String accessToken, String path) {
		//是否选择了关注对象
		AssertUtil.isTrue(dinerId == null || dinerId < 1, "请选择要查看的人");
		//获取用户登录信息
		SignInDinerInfo dinerInfo = loadSignInDinerInfo(accessToken);
		//登录用户的关注信息
		String loginDinerKey = RedisKeyConstant.following.getKey() + dinerInfo.getId();
		//登录用户关注的对象的粉丝
		String dinerKey = RedisKeyConstant.following.getKey() + dinerId;
		//计算交集
		Set<Integer> followingDinerIds = redisTemplate.opsForSet().intersect(loginDinerKey, dinerKey);
		//没有
		if (followingDinerIds == null || followingDinerIds.isEmpty()) {
			return ResultInfoUtil.buildSuccess(path, new ArrayList<ShortDinerInfo>());
		}

		//根据ids 查询食客信息
		ResultInfo resultInfo = restTemplate
				.getForObject(dinersServerName + "findByIds?access_token=${accessToken}&ids={ids}", ResultInfo.class,
						accessToken, StrUtil.join(",", followingDinerIds));
		if (resultInfo.getCode() != ApiConstant.SUCCESS_CODE) {
			resultInfo.setPath(path);
			return resultInfo;
		}
		//处理结果集
		List<LinkedHashMap> dinerInfoMaps = (ArrayList)resultInfo.getData();
		List<ShortDinerInfo> collect = dinerInfoMaps.stream()
				.map(diner -> fillBeanWithMap(diner, new ShortDinerInfo(), true))
				.collect(Collectors.toList());
		return ResultInfoUtil.buildSuccess(path,collect);
	}

	/**
	 * 获取我的关注列表，以及关注我的人列表
	 * @param accessToken
	 * @param path
	 * @param type
	 * @return
	 */
	public ResultInfo myList(String accessToken, String path, Integer type) {
		//获取用户登录信息
		SignInDinerInfo dinerInfo = loadSignInDinerInfo(accessToken);
		String loginDinerKey="";
		if(type ==1){
			//我的粉丝列表
			//登录用户的关注信息
			loginDinerKey = RedisKeyConstant.following.getKey() + dinerInfo.getId();
		}else {
			//别人关注我的，也就是我的粉丝
			loginDinerKey = RedisKeyConstant.followers.getKey() + dinerInfo.getId();
		}


		Set<Integer> members = redisTemplate.opsForSet().members(loginDinerKey);
		//没有
		if (members == null || members.isEmpty()) {
			return ResultInfoUtil.buildSuccess(path, new ArrayList<ShortDinerInfo>());
		}
		ResultInfo resultInfo = restTemplate
				.getForObject(dinersServerName + "findByIds?access_token=${accessToken}&ids={ids}", ResultInfo.class,
						accessToken, StrUtil.join(",", members));
		if (resultInfo.getCode() != ApiConstant.SUCCESS_CODE) {
			resultInfo.setPath(path);
			return resultInfo;
		}
		List<LinkedHashMap> linkedHashMaps = (ArrayList)resultInfo.getData();
		List<ShortDinerInfo> collect = linkedHashMaps.stream().map(x -> fillBeanWithMap(x, new ShortDinerInfo(), true))
				.collect(Collectors.toList());
		return ResultInfoUtil.buildSuccess(path,collect);
	}

	/**
	 * 判断用户是否关注
	 * @param accessToken
	 * @param servletPath
	 * @param dinerId
	 * @return
	 */
	public ResultInfo checkFollow(String accessToken, String servletPath, Integer dinerId) {
		//获取用户登录信息
		SignInDinerInfo dinerInfo = loadSignInDinerInfo(accessToken);
		String loginDinerKey = RedisKeyConstant.following.getKey() + dinerInfo.getId();

		Boolean member = redisTemplate.opsForSet().isMember(loginDinerKey, dinerId);
		return ResultInfoUtil.buildSuccess(servletPath,member);
	}
	/**
	 * 获取粉丝列表
	 *
	 * @param dinerId
	 * @return
	 */
	public Set<Integer> findFollowers(Integer dinerId) {
		AssertUtil.isNotNull(dinerId, "请选择查看的用户");
		Set<Integer> members = redisTemplate.opsForSet().members(RedisKeyConstant.followers.getKey() + dinerId);
		return members;
	}

	/**
	 * 发送请求添加或者移除关注人的Feed列表
	 *
	 * @param followDinerId 关注好友的ID
	 * @param accessToken   当前登录用户token
	 * @param type          0=取关 1=关注
	 */
	private void sendSaveOrRemoveFeed(Integer followDinerId,String accessToken,int type){
		String feedsUpdateUrl = feedsServerName + "updateFollowingFeeds/" + followDinerId +
				"?access_token="+accessToken;
		//构建请求头
		HttpHeaders headers = new HttpHeaders();
		headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
		//构建请求体
		MultiValueMap<String,Object> body = new LinkedMultiValueMap<>();
		body.add("type",type);
		HttpEntity<MultiValueMap<String,Object>> entity = new HttpEntity<>(body,headers);
		restTemplate.postForEntity(feedsUpdateUrl,entity,ResultInfo.class);
	}

}
